#include "lcd_drv.h"

#if (LCD_CONNECTION == LCD_CONNECTION_PCF8574)
//	Para o pcf8574 a comunicação é 4 bits e consideramos que as seguintes ligações:
//		P0: LCD_D4
//		P1: LCD_D5
//		P2: LCD_D6
//		P3: LCD_D7
//		P4: LCD_RS
//		P5: LCD_RW
//		P6: LCD_E

// Lê um comando/dado do display.
//	ATENÇÃO: Como o barramento I2C é lento, não precisamos de delays entre os comandos
//		wr: 1: Sinaliza leitura
//		e:	0: Disabilita acesso ao display
//			1: Habilita acesso ao display
// 		rs: 0: Sinaliza ler um comando
//			1: Sinaliza ler um dado
u8 lcd_read(u8 rs) {
	// LCD    	   E  RW RS      DATA
	// lpc8574 	D7 D6 D5 D4   D3 D2 D1 D0
	u8 lsn=0, msn=0;
	u8 cmd;

	// WR = 1
	cmd = 0x20;
//	lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//	delay_us(LCD_DELAY);

	// RS = rs
	if (rs)	cmd |= 0x10;	// RS = 1
	lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//	delay_us(LCD_DELAY);

	// E = 1
	cmd |= 0x40;
	lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//	delay_us(LCD_DELAY);

	// Lê nibble alto do dado do BUS
	lcd_Receive(LCD_ADDR_I2C, &msn , 1);

	// E = 0
	cmd &= ~0x40;
	lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//	delay_us(LCD_DELAY);

	// E = 1
	cmd |= 0x40;
	lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//	delay_us(LCD_DELAY);

	// Lê nibble baixo do dado do BUS
	lcd_Receive(LCD_ADDR_I2C, &lsn , 1);

	// E = 0
	cmd &= ~0x40;
	lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//	delay_us(LCD_DELAY);

	return ( (msn<<4) | lsn);
}

// Escreve um comando ou dado no barramento do lcd.
//	ATENÇÃO: Como o barramento I2C é lento, não precisamos de delays entre os comandos
//		wr: 0: Sinaliza escrita
//		e:	0: Disabilita acesso ao display
//			1: Habilita acesso ao display
// 		rs: 0: Sinaliza escrever um comando
//			1:Sinaliza escrever um dado
void lcd_write(u8 value, u8 rs, u8 dualAccess) {
	// LCD    	   E  RW RS      DATA
	// lpc8574 	D7 D6 D5 D4   D3 D2 D1 D0
	u8 cmd;

	// WR = 0
	cmd = 0;
//	lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//	delay_us(LCD_DELAY);

	// RS = rs
	if (rs)	cmd |= 0x10;
//	lcd_Send(LCD_ADDR_I2C, &cmd , 1); // RS = 1
//	delay_us(LCD_DELAY);

	// BUS = nibble alto do dado
	cmd |= (value >> 4);
	lcd_Send(LCD_ADDR_I2C, &cmd , 1);

	// E = 1
	cmd |= 0x40;
	lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//	delay_us(LCD_DELAY);
	// E = 0
	cmd &= ~0x40;
	lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//	delay_us(LCD_DELAY);

	if (dualAccess) {
		// BUS = nibble baixo do dado
		cmd &= 0xF0;
		cmd |= (value&0xF);
		lcd_Send(LCD_ADDR_I2C, &cmd , 1);

		// E = 1
		cmd |= 0x40;
		lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//		delay_us(LCD_DELAY);
		// E = 0
		cmd &= ~0x40;
		lcd_Send(LCD_ADDR_I2C, &cmd , 1);
//		delay_us(LCD_DELAY);
	}
}

#elif (LCD_CONNECTION == LCD_CONNECTION_GPIO_4BITS)
void lcd_config_ports(void)	{
	LCD_PORTDATA_DIR &= ~(LCD_PIN_D7|LCD_PIN_D6|LCD_PIN_D5|LCD_PIN_D4); // Pinos dos dados como entrada
	LCD_PORTCTRL_DIR |= (LCD_PIN_RS|LCD_PIN_WR|LCD_PIN_E); // Pinos de controle como saida
	LCD_PORTCTRL_CLR |= LCD_PIN_E;
}

// Lê um comando/dado do display
//		wr: 1: Sinaliza leitura
//		e:	0: Disabilita acesso ao display
//			1: Habilita acesso ao display
// 		rs: 0: Sinaliza ler um comando
//			1: Sinaliza ler um dado
u8 lcd_read(u8 rs) {
	u8 value = 0;

	LCD_PORTCTRL_SET = LCD_PIN_WR;			// WR = 1;
	delay_us(LCD_DELAY);

	if (rs)	LCD_PORTCTRL_SET = LCD_PIN_RS;	// RS = 1;
	else	LCD_PORTCTRL_CLR = LCD_PIN_RS;	// RS = 0;
	delay_us(LCD_DELAY);

	LCD_PORTCTRL_SET = LCD_PIN_E;			// E = 1
	delay_us(LCD_DELAY);

	if (LCD_PORTDATA_IN&LCD_PIN_D7)		value|=0x80;
	if (LCD_PORTDATA_IN&LCD_PIN_D6)		value|=0x40;
	if (LCD_PORTDATA_IN&LCD_PIN_D5)		value|=0x20;
	if (LCD_PORTDATA_IN&LCD_PIN_D4)		value|=0x10;

	LCD_PORTCTRL_CLR = LCD_PIN_E;			// E = 0
	delay_us(LCD_DELAY);
	LCD_PORTCTRL_SET = LCD_PIN_E;			// E = 1
	delay_us(LCD_DELAY);

	if (LCD_PORTDATA_IN&LCD_PIN_D7)		value|=0x8;
	if (LCD_PORTDATA_IN&LCD_PIN_D6)		value|=0x4;
	if (LCD_PORTDATA_IN&LCD_PIN_D5)		value|=0x2;
	if (LCD_PORTDATA_IN&LCD_PIN_D4)		value|=0x1;

	LCD_PORTCTRL_CLR = LCD_PIN_E;			// E = 0
	delay_us(LCD_DELAY);
	return value;
}

// Escreve um comando ou dado no barramento do lcd
//		wr: 0: Sinaliza escrita
//		e:	0: Disabilita acesso ao display
//			1: Habilita acesso ao display
// 		rs: 0: Sinaliza escrever um comando
//			1:Sinaliza escrever um dado
void lcd_write(u8 value, u8 rs, u8 dualAccess) {
	LCD_PORTDATA_DIR |= (LCD_PIN_D7|LCD_PIN_D6|LCD_PIN_D5|LCD_PIN_D4); // Pinos dos dados como saida

	LCD_PORTCTRL_CLR = LCD_PIN_WR;			// WR = 0;
	delay_us(LCD_DELAY);

	if (rs)	LCD_PORTCTRL_SET = LCD_PIN_RS;	// RS = 1;
	else	LCD_PORTCTRL_CLR = LCD_PIN_RS;	// RS = 0;
	delay_us(LCD_DELAY);

	if (value&0x80) LCD_PORTDATA_SET = LCD_PIN_D7; else LCD_PORTDATA_CLR = LCD_PIN_D7;
	if (value&0x40) LCD_PORTDATA_SET = LCD_PIN_D6; else LCD_PORTDATA_CLR = LCD_PIN_D6;
	if (value&0x20) LCD_PORTDATA_SET = LCD_PIN_D5; else LCD_PORTDATA_CLR = LCD_PIN_D5;
	if (value&0x10) LCD_PORTDATA_SET = LCD_PIN_D4; else LCD_PORTDATA_CLR = LCD_PIN_D4;

	LCD_PORTCTRL_SET = LCD_PIN_E;	// E = 1
	delay_us(LCD_DELAY);
	LCD_PORTCTRL_CLR = LCD_PIN_E;	// E = 0
	delay_us(LCD_DELAY);

	if (dualAccess) {
		if (value&0x8) LCD_PORTDATA_SET = LCD_PIN_D7; else LCD_PORTDATA_CLR = LCD_PIN_D7;
		if (value&0x4) LCD_PORTDATA_SET = LCD_PIN_D6; else LCD_PORTDATA_CLR = LCD_PIN_D6;
		if (value&0x2) LCD_PORTDATA_SET = LCD_PIN_D5; else LCD_PORTDATA_CLR = LCD_PIN_D5;
		if (value&0x1) LCD_PORTDATA_SET = LCD_PIN_D4; else LCD_PORTDATA_CLR = LCD_PIN_D4;

		LCD_PORTCTRL_SET = LCD_PIN_E;	// E = 1
		delay_us(LCD_DELAY);
		LCD_PORTCTRL_CLR = LCD_PIN_E;	// E = 0
		delay_us(LCD_DELAY);
	}

	LCD_PORTDATA_DIR &= ~(LCD_PIN_D7|LCD_PIN_D6|LCD_PIN_D5|LCD_PIN_D4); // Pinos dos dados como entrada
}
#elif (LCD_CONNECTION == LCD_CONNECTION_GPIO_8BITS)
void lcd_config_ports(void)	{
	LCD_PORTDATA_DIR &= ~(LCD_PIN_D7|LCD_PIN_D6|LCD_PIN_D5|LCD_PIN_D4|LCD_PIN_D3|LCD_PIN_D2|LCD_PIN_D1|LCD_PIN_D0); // Pinos dos dados como entrada
	LCD_PORTCTRL_DIR |= (LCD_PIN_RS|LCD_PIN_WR|LCD_PIN_E); // Pinos de controle como saida
	LCD_PORTCTRL_CLR |= LCD_PIN_E;
}

// Lê um comando/dado do display
//		wr: 1: Sinaliza leitura
//		e:	0: Disabilita acesso ao display
//			1: Habilita acesso ao display
// 		rs: 0: Sinaliza ler um comando
//			1: Sinaliza ler um dado
u8 lcd_read(u8 rs) {
	u8 value = 0;

	LCD_PORTCTRL_SET = LCD_PIN_WR;			// WR = 1;
	if (rs)	LCD_PORTCTRL_SET = LCD_PIN_RS;	// RS = 1;
	else	LCD_PORTCTRL_CLR = LCD_PIN_RS;	// RS = 0;
	LCD_PORTCTRL_SET = LCD_PIN_E;			// E = 1
	delay_us(LCD_DELAY);

	if (LCD_PORTDATA_IN&LCD_PIN_D7)		value|=0x80;
	if (LCD_PORTDATA_IN&LCD_PIN_D6)		value|=0x40;
	if (LCD_PORTDATA_IN&LCD_PIN_D5)		value|=0x20;
	if (LCD_PORTDATA_IN&LCD_PIN_D4)		value|=0x10;
	if (LCD_PORTDATA_IN&LCD_PIN_D3)		value|=0x08;
	if (LCD_PORTDATA_IN&LCD_PIN_D2)		value|=0x04;
	if (LCD_PORTDATA_IN&LCD_PIN_D1)		value|=0x02;
	if (LCD_PORTDATA_IN&LCD_PIN_D0)		value|=0x01;

	LCD_PORTCTRL_CLR = LCD_PIN_E;			// E = 0

	return value;
}

// Escreve um comando ou dado no barramento do lcd
//		wr: 0: Sinaliza escrita
//		e:	0: Disabilita acesso ao display
//			1: Habilita acesso ao display
// 		rs: 0: Sinaliza escrever um comando
//			1:Sinaliza escrever um dado
void lcd_write(u8 value, u8 rs) {
	LCD_PORTDATA_DIR |= (LCD_PIN_D7|LCD_PIN_D6|LCD_PIN_D5|LCD_PIN_D4|LCD_PIN_D3|LCD_PIN_D2|LCD_PIN_D1|LCD_PIN_D0); // Pinos dos dados como saida
	LCD_PORTCTRL_CLR = LCD_PIN_WR;			// WR = 0;

	if (rs)	LCD_PORTCTRL_SET = LCD_PIN_RS;	// RS = 1;
	else	LCD_PORTCTRL_CLR = LCD_PIN_RS;	// RS = 0;

	// Atualizar barramento do LCD com o valor do registrador
	if (value&0x80) LCD_PORTDATA_SET = LCD_PIN_D7; else LCD_PORTDATA_CLR = LCD_PIN_D7;
	if (value&0x40) LCD_PORTDATA_SET = LCD_PIN_D6; else LCD_PORTDATA_CLR = LCD_PIN_D6;
	if (value&0x20) LCD_PORTDATA_SET = LCD_PIN_D5; else LCD_PORTDATA_CLR = LCD_PIN_D5;
	if (value&0x10) LCD_PORTDATA_SET = LCD_PIN_D4; else LCD_PORTDATA_CLR = LCD_PIN_D4;
	if (value&0x08) LCD_PORTDATA_SET = LCD_PIN_D3; else LCD_PORTDATA_CLR = LCD_PIN_D3;
	if (value&0x04) LCD_PORTDATA_SET = LCD_PIN_D2; else LCD_PORTDATA_CLR = LCD_PIN_D2;
	if (value&0x02) LCD_PORTDATA_SET = LCD_PIN_D1; else LCD_PORTDATA_CLR = LCD_PIN_D1;
	if (value&0x01) LCD_PORTDATA_SET = LCD_PIN_D0; else LCD_PORTDATA_CLR = LCD_PIN_D0;

	LCD_PORTCTRL_SET = LCD_PIN_E;	// E = 1
	delay_us(LCD_DELAY);
	LCD_PORTCTRL_CLR = LCD_PIN_E;	// E = 0
	delay_us(LCD_DELAY);
	LCD_PORTDATA_DIR &= ~(LCD_PIN_D7|LCD_PIN_D6|LCD_PIN_D5|LCD_PIN_D4|LCD_PIN_D3|LCD_PIN_D2|LCD_PIN_D1|LCD_PIN_D0); // Pinos dos dados como entrada
}
#endif
